class h_order {

	async preprocessOrder(orderData = {}) {
		const {storeId, tableNum, orderPayMode, orderRemark = '', orderCouponData, orderGoodsData} = orderData
		if(!this._checkStoreId(storeId))
			return this.reject('-2014', 'storeId invalid')
		if(!this._checkTableNum(tableNum))
			return this.reject('-2019', 'table number invalid')
		if([0, 1, 2].indexOf(parseInt(orderPayMode)) == -1)
			return this.reject('-2029', 'order pay mode invalid') 
		if(orderRemark.length > 100)
			return this.reject('-2030', 'order remark too long')
		const goodsIds = Object.keys(orderGoodsData)
		if(goodsIds.length == 0)
			return this.reject('-2020', 'goods list is empty')
		console.log(orderGoodsData, goodsIds)
		if(!await this.mysql.store.tableExists(`${storeId}_menu`))
			return this.reject('-2021', 'store menu not found')
		const menu = await this.mysql.store.select(`${storeId}_menu`, 'goodsId, goodsName, goodsSpec, goodsSellOut, goodsPut', `goodsId in (${goodsIds.join(`,`).replace(/[a-zA-Z0-9]{6}/g, '?')}) and goodsFreeze=0`, goodsIds)
		const result = await this._handleOrderData(menu, orderGoodsData, orderCouponData)
		return result
	}

	async _handleOrderData(menu = [], order, coupon = {}) {
		let total = 0
		let detail = []
		for(let goods of menu) {
			if(!goods.goodsPut)
				return this.reject('-2025', 'goods is pull off')
			if(goods.goodsSellOut)
				return this.reject('-2026', 'goods is sold out')
			for(let orderGoods of order[goods.goodsId]) {
				let goodsName = goods.goodsName
				let goodsPrice = 0
				if(this.common.isExists(orderGoods.chooseGoodsSpec)) {
					if(!isNaN(goods.goodsSpec))
						return this.reject('-2022', 'goods spec data invalid')
					let spec = JSON.parse(goods.goodsSpec)
					if(!spec[orderGoods.chooseGoodsSpec])
						return this.reject('-2023', 'goods spec not found')
					goodsName += `(${spec[orderGoods.chooseGoodsSpec].n})`
					goodsPrice = spec[orderGoods.chooseGoodsSpec].p
				}
				else {
					if(isNaN(goods.goodsSpec))
						return this.reject('-2022', 'goods spec data invalid')
					goodsPrice = goods.goodsSpec
				}
				if(goodsPrice != orderGoods.goodsPrice)
					return this.reject('-2024', 'order goods price error')
				total += goodsPrice * orderGoods.goodsCount
				detail.push({
					i: goods.goodsId,
					n: goodsName,
					p: goodsPrice,
					c: orderGoods.goodsCount
				})
			}
		}
		return {
			orderTotal: total,
			orderPayTotal: total,
			orderGoods: JSON.stringify(detail),
			orderCoupon: ''
		}
	}

	async getFinishOrderDataByOrderId(orderId) {
		if(!this._checkOrderId(orderId))
			return this.reject('-2057', 'order id is invalid')
		const finishOrder = await this.mysql.order.select('finish_order', '*', 'orderId=?', [orderId])
		if(!finishOrder[0])
			return this.reject('-2061', 'finish order not found')
		return finishOrder[0]
	}

	async createOrder(preOrderData) {
		const {userType, userId, userName, userRealName, userPhone, storeId, tableNum, orderPayMode, orderTotal, orderPayTotal, orderGoods, orderCoupon, orderRemark} = preOrderData
		const orderId = await this._createOrderId()
		const orderDate = parseInt(Date.now() / 1000)
		let createResult = await this.mysql.order.insert('wait_order', 'orderId, orderType, orderGoods, orderTotal, orderPayMode, orderPayTotal, orderDate, orderCoupon, orderStore, orderTableNum, orderRemark, orderUserType, orderUser, orderUserName', [
			orderId, 0, orderGoods, orderTotal, orderPayMode, orderPayTotal, orderDate, orderCoupon, storeId, tableNum, orderRemark, userType, userId, (userRealName == '' ? userName : userRealName)
		])
		if(!createResult)
			return this.reject('-2031', 'create new order failed')
		return orderId
	}

	async cashPayOrder(orderId) {
		if(!this._checkOrderId(orderId))
			return this.reject('-2057', 'order id is invalid')
		if(!await this.mysql.order.update('wait_order', 'orderPayed=1, orderPayDate=?', 'orderId=?', [this.common.timestamp(), orderId]))
			return this.reject('-2037', 'cash pay handle failed')
	}

	async wxPayOrder(orderId, orderData) {
		if(!this._checkOrderId(orderId))
			return this.reject('-2057', 'order id is invalid')
		const {appId, serviceAppId, merchantId, serviceMerchantId, servicePayKey, payNotifyUrl, prePayExpire} = this.config.miniApp.gtClient
		const {wxUserId, userIP, storeData, orderPayTotal, orderCoupon} = orderData
		const nonceStr = await this.common.createRandomStr(32)
		const {startTime, endTime} = this.common.getSpaceTime(prePayExpire)
		const payData = this._buildWxPrePayData({
			appid: serviceAppId,
			mch_id: serviceMerchantId,
			sub_appid: appId,
			sub_mch_id: merchantId,
			sub_openid: wxUserId,
			attach: `${storeData.storeId}`,
			body: `${storeData.storeName}-点餐`,
			out_trade_no: orderId,
			total_fee: orderPayTotal,
			notify_url: payNotifyUrl,
			trade_type: 'JSAPI',
			spbill_create_ip: userIP,
			nonce_str: nonceStr,
			time_start: startTime,
			time_expire: endTime
			//goods_tag: undefined
		})
		console.log(payData)
		const prePayResult = await this.common.reqPost({
			url: 'https://api.mch.weixin.qq.com/pay/unifiedorder',
			headers: {
				'content-type': 'application/xml'
			},
			data: payData
		})
		console.log(prePayResult)
		const {xml} = await this.common.xml2js(prePayResult)
		console.log(xml)
		if(!this._checkWxPayData(xml))
			return this.reject('-2052', 'check prepay back wx data invalid')
		const {return_code, return_msg, result_code, prepay_id} = xml
		if(return_code == 'SUCCESS' && return_msg == 'OK' && result_code == 'SUCCESS') {
			await this._updateOrderPrePayData(orderId, prepay_id, prePayExpire)
			const prePayData = this._buildWxAppPrePayData(appId, nonceStr, prepay_id, servicePayKey)
			return prePayData
		}
		else {
			console.error('prepay order failed:', return_code, return_msg)
			this.common.writeLog('error', `prepay order failed:${return_code} ${return_msg}`)
			return this.reject('-2054', 'prepay order failed')
		}
	}

	async reWxPayOrder(orderId, userId) {
		if(!this._checkOrderId(orderId))
			return this.reject('-2057', 'order id is invalid')
		const notPayOrder = await this.mysql.order.select('wait_order', 'orderPrePayId, orderPrePayExpire', 'orderPayed=0 and orderUser=? and orderId=?', [userId, orderId])
		if(!notPayOrder[0] || !this._checkWxPrePayId(notPayOrder[0].orderPrePayId))
			return this.reject('-2059', 'repay order is invalid')
		const {orderPrePayId, orderPrePayExpire} = notPayOrder[0]
		if(this.common.timestamp() > orderPrePayExpire)
			return this.reject('-2061', 'prepay is expire')
		const {appId, servicePayKey} = this.config.miniApp.gtClient
		const nonceStr = await this.common.createRandomStr(32)
		const prePayData = this._buildWxAppPrePayData(appId, nonceStr, orderPrePayId, servicePayKey)
		return prePayData
	}

	async cancelWxPayOrder(orderId, userId) {
		if(!this._checkOrderId(orderId))
			return this.reject('-2057', 'order id is invalid')
		const result = await this.mysql.order.delete('wait_order', 'orderPayed=0 and userId=? and orderId=?', [userId, orderId])
		if(result == 0)
			return this.reject('-2058', 'cancel wait order failed')
	}

	async refundWxPayOrder(orderId) {
		if(!this._checkOrderId(orderId))
			return this.reject('-2057', 'order id is invalid')
	}

	async takeOrder(orderId, printers = []) {
		if(!this._checkOrderId(orderId))
			return this.reject('-2057', 'order id is invalid')
		const waitOrder = await this.mysql.order.select('wait_order', '*', 'orderId=?', [orderId])
		const {wxOrderId, orderType, orderGoods, orderTotal, orderPayed, orderPayMode, orderPayTotal, orderPayDate, orderCoupon, orderStore, orderTableNum, orderRemark, orderUserType, orderUser, orderUserName, orderPrePayId} = waitOrder[0]
		if(!orderPayed)
			return this.reject('-2045', 'order not pay')
		let orderPrinters = ''
		if(printers.length > 0) {
			for(let printer of printers) {
				orderPrinters += `,${printer.printerId}`
			}
			orderPrinters = this.common.ltrim(orderPrinters, ',')
		}
		const orderDate = this.common.timestamp()
		const result = await this.mysql.order.insert('finish_order', 'orderId, wxOrderId, orderType, orderGoods, orderTotal, orderPayMode, orderPayTotal, orderPayDate, orderDate, orderCoupon, orderStore, orderTableNum, orderRemark, orderUserType, orderUser, orderUserName, orderPoint, orderPrinters', [
			orderId, wxOrderId, orderType, orderGoods, orderTotal, orderPayMode, orderPayTotal, orderPayDate, orderDate, orderCoupon, orderStore, orderTableNum, orderRemark, orderUserType, orderUser, orderUserName, 'Uk2A', orderPrinters
		])
		if(result == 0)
			return this.reject('-2047', 'store take order failed')
		await this.delWaitOrder(orderId)
		return {
			orderType,
			orderGoods,
			orderTotal,
			orderPayTotal,
			orderPayMode,
			orderDate,
			orderCoupon,
			orderStore,
			orderRemark,
			orderTableNum,
			orderUserType,
			orderUserName,
			orderPrePayId
		}
	}

	async wxAppCheckOrderPay(orderId, userId) {
		if(!this._checkOrderId(orderId))
			return this.reject('-2057', 'order id is invalid')
		const waitOrder = await this.mysql.order.select('wait_order', 'orderId', 'orderUser=? and orderId=?', [userId, orderId])
		if(!waitOrder[0]) {
			const finishOrder = await this.mysql.order.select('finish_order', 'orderId', 'orderUser=? and orderId=?', [userId, orderId])
			if(!finishOrder[0])
				return false
		}
		return true
	}

	async storeCheckOrderPay(orderId, storeId) {
		if(!this._checkOrderId(orderId))
			return this.reject('-2057', 'order id is invalid')
		if(!this._checkStoreId(storeId))
			return this.reject('-2014', 'storeId invalid')
		const waitOrder = await this.mysql.order.select('wait_order', 'orderId', 'orderStore=? and orderId=?', [storeId, orderId])
		if(!waitOrder[0]) {
			const finishOrder = await this.mysql.order.select('finish_order', 'orderId', 'orderStore=? and orderId=?', [storeId, orderId])
			if(!finishOrder[0])
				return false
		}
		return true
	}

	async insideCheckOrderPay(orderId) {
		if(!this._checkOrderId(orderId))
			return this.reject('-2057', 'order id is invalid')
		const waitOrder = await this.mysql.order.select('wait_order', 'orderId', 'orderId=?', [storeId, orderId])
		if(!waitOrder[0]) {
			const finishOrder = await this.mysql.order.select('finish_order', 'orderId', 'orderId=?', [storeId, orderId])
			if(!finishOrder[0])
				return false
		}
		return true
	}

	async delWaitOrder(orderId) {
		if(!this._checkOrderId(orderId))
			return this.reject('-2057', 'order id is invalid')
		const waitOrder = await this.mysql.order.select('wait_order', 'orderId', 'orderId=?', [orderId])
		if(waitOrder[0]) {
			const result = await this.mysql.order.delete('wait_order', 'orderId=?', [orderId])
			return result > 0
		}
		else
			return this.reject('-2048', 'wait order not found')
	}

	async delWaitOrderByUserId(orderId, userId) {
		if(!this._checkOrderId(orderId))
			return this.reject('-2057', 'order id is invalid')
		const waitOrder = await this.mysql.order.select('wait_order', 'orderId', 'orderId=? and userId=?', [orderId, userId])
		if(waitOrder[0]) {
			const result = await this.mysql.order.delete('wait_order', 'orderId=? and userId=?', [orderId, userId])
			return result > 0
		}
		else
			return this.reject('-2048', 'wait order not found')
	}

	async buildPrintContent(storeData, orderData, printTemplate) {
		const {storeName} = storeData
		const {orderType, orderGoods, orderTotal, orderPayTotal, orderPayMode, orderDate, orderCoupon, orderStore, orderRemark, orderTableNum, orderUserType, orderUserName, orderPoint} = orderData
		let goodsList = JSON.parse(orderGoods)
		const orderPrintData = {
			orderType, 
			orderGoods: ((_goodsList) => {
				let content = ''
				for(let goods of _goodsList) {
					content += `<tr><td>${goods.n}</td><td>${goods.c}</td><td>￥${goods.p / 100}</td></tr>`
				}
				return content
			})(goodsList),
			orderGoodsBig: ((_goodsList) => {
				let content = ''
				for(let goods of _goodsList) {
					content += `${goods.n} × ${goods.c}\r`
				}
				return content
			})(goodsList),
			orderTotal: orderTotal / 100, 
			orderPayTotal: orderPayTotal / 100, 
			orderCouponTotal: (orderTotal - orderPayTotal) / 100,
			orderDate: this.common.timestamp2str(orderDate, true, true, true, true, true, true, '/'), 
			orderCoupon,
			orderPayMode: ['现金支付', '微信支付'][orderPayMode] || '未知' ,
			orderRemark,
			orderTableNum, 
			orderUserName, 
			orderPoint,
			storeName
		}
		for(let key of Object.keys(orderPrintData))
			printTemplate = printTemplate.replace(new RegExp(`{{${key}}}`, 'g'), orderPrintData[key])
		return printTemplate
	}

	async updateOrderPrintStatus(orderId, printerCode, printStatus, printTime) {
		if(!this._checkOrderId(orderId))
			return this.reject('-2057', 'order id is invalid')
		await this.mysql.order.update('finish_order', 'orderFinishPrint=1, orderFinishPrintTime=?', 'orderId=?', [printTime, orderId])
	}

	async checkWxPayData(payData) {
		return await this._checkWxPayData(payData)
	}

	async updateOrderPayStatus(updateData) {
		const {orderId, storeId, wxOrderId, orderPayTotal, orderPayDate} = updateData
		if(!this._checkOrderId(orderId))
			return this.reject('-2057', 'order id is invalid')
		const result = await this.mysql.order.update('wait_order', 'orderPayed=1, orderPayDate=?', 'orderStore=? and orderId=?', [orderPayDate, storeId, orderId])
		if(result == 0) 
			return false
		return true
	}

	async _createOrderId() {
		return  `${await this.common.createRandomStr(19)}${Date.now().toString()}`
	}

	_buildWxPrePayData(data) {
		try {
			const { servicePayKey } = this.config.miniApp.gtClient
			let keys = Object.keys(data)
			keys = keys.sort()
			let xmlStr = '<xml>'
			let signStr = ''
			for(let key of keys) {
				xmlStr += `<${key}>${data[key]}</${key}>`
				signStr += `${key}=${data[key]}&`
			}
			signStr = `${signStr}key=${servicePayKey}`
			xmlStr += `<sign>${this.common.md5(signStr)}</sign></xml>`
			return xmlStr
		}
		catch(err) {
			console.error(err)
			throw {code:'-2051',msg:'get sign pay data failed'}
		}
	}

	_checkWxPayData(payData) {
		try {
			const {servicePayKey} = this.config.miniApp.gtClient
			let sign = '', signStr = ''
			let data = Object.keys(payData)
			data = data.sort()
			for(let key of data) {
				if(key == 'sign') {
					sign = payData[key]
					continue
				}
				signStr += `${key}=${payData[key]}&`
			}
			signStr = `${signStr}key=${servicePayKey}`
			let _sign = this.common.md5(signStr)
			console.log(sign, _sign)
			if(sign == _sign.toUpperCase()) {
				console.log('prepay order data accept')
				return true
			}
			else {
				return false
			}
		}
		catch(err) {
			console.error(err)
			throw {code:'-2053',msg:'check prepay back wx data failed'}
		}
	}

	async _updateOrderPrePayData(orderId, prePayId, prePayExpire) {
		if(!this._checkOrderId(orderId))
			return this.reject('-2057', 'order id is invalid')
		return await this.mysql.order.update('wait_order', 'orderPrePayId=?, orderPrePayExpire=?', 'orderId=?', [prePayId, (parseInt(this.common.timestamp()) + parseInt(prePayExpire)), orderId])
	}

	_buildWxAppPrePayData(appId, nonceStr, prePayId, servicePayKey) {
		try {
			const _package = `prepay_id=${prePayId}`
			const timestamp = this.common.timestamp()
			const signStr = `appId=${appId}&nonceStr=${nonceStr}&package=${_package}&signType=MD5&timeStamp=${timestamp}&key=${servicePayKey}`
			const sign = this.common.md5(signStr)
			return {
				timestamp,
				nonceStr,
				package: _package,
				signType: 'MD5',
				paySign: sign.toUpperCase()
			}
		}
		catch(err) {
			console.error(err)
			throw {code:'-2055', msg:'build wx pay data failed'}
		}
	}

	_checkStoreId(storeId) {
		return /^[a-zA-Z0-9]{10}$/.test(storeId)
	}

	_checkOrderId(orderId) {
		return /^[a-zA-Z0-9]{19}\d{13}$/.test(orderId)
	}

	_checkTableNum(tableNum) {
		return tableNum && tableNum.length > 0 && tableNum.length < 10
	}

	_checkWxPrePayId(prePayId) {
		return /^wx[a-z0-9]{34,62}$/.test(prePayId)
	}

}

module.exports = h_order